package com.xand.jsonrpc.server;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.reflections.Reflections;

import com.xand.jsonrpc.JSONGenericRequest;
import com.xand.jsonrpc.JSONGenericResponse;
import com.xand.jsonrpc.JSONSerializer;

public class JSONRPCManager {
	private JSONSerializer serializer;

	private HashMap<String, JSONServiceDefinition> services;

	public JSONRPCManager() throws Exception {
		services = new HashMap<String, JSONServiceDefinition>();

		serializer = new JSONSerializer("dskfjklsjflksdjfldksjflkdsjklsdfjdslkjf");
		configure();
	}
	
	public String invoke(String req) throws Exception {
		return invoke(req, new HashMap());
	}
	
	public String invoke(String req, Map context) throws Exception {
		JSONGenericRequest request = (JSONGenericRequest)serializer.deserialize(req, JSONGenericRequest.class);
		
		return invoke(request, context);
	}
	
	public String invoke(JSONGenericRequest request, Map context) throws Exception {
		String serviceId = request.getServiceId();
		
		JSONServiceDefinition service = services.get(serviceId);
		
		JSONGenericResponse resp = service.invoke(request, context);
		
		String ret = serializer.serializeResponse(resp);
		
		return ret;
	}

	private void configure() throws Exception {
		// exploro el classpath en busca de los métodos anotados
		// con @Service
		Reflections reflections = new Reflections("");
		Set<Class<? extends GenericServiceExecutor>> subTypes = reflections.getSubTypesOf(GenericServiceExecutor.class);

		Iterator<Class<?extends GenericServiceExecutor>> iter = subTypes.iterator();
		Class clazz;
		Service annot;
		String className; // nombre completo
		JSONServiceDefinition service;
		String in;
		String out;
		HashMap instClasses = new HashMap();
		while(iter.hasNext()) {
			// a cargar
			clazz = iter.next();
			// escaneo los métodos de esta clase
			Method[] ms = clazz.getMethods();
			for(Method m : ms) {
				Annotation[] as = m.getAnnotations();
				for(Annotation a : as) {
					if("com.xand.jsonrpc.server.Service".equals(a.annotationType().getName())) {
						Class[] parameters = m.getParameterTypes();
						if(parameters.length == 2) {
							Class retType = m.getReturnType();

							Object check = services.get(m.getName());
							if(check == null) {
								// ya tenemos nuestro servicio, vamos a cargarlo
								service = new JSONServiceDefinition();
								service.setExecutorClass(clazz.getName());
								service.setId(m.getName());
								service.setIn(parameters[0].getName());
								service.setOut(retType.getName());

								// primero busco si la clase ya está instanciada
								Object inst = instClasses.get(clazz.getName());
								if(inst == null) {
									service.startExecutor();
									inst = service.getInstance();
									instClasses.put(clazz.getName(), inst);
								}else{
									service.setInstance(inst);
									service.startExecutor();
								}

								services.put(service.getId(), service);
							}else{
								throw new Exception("Nombre de servicio repetido -> " + m.getName());
							}
						}
					}
				}
			}
		}
	}
}
